#!/usr/bin/env python
# by h3k ( michael vandenberghe )and Inc (Ian Caldwell)
import sys
sys.path.append("/usr/share/capsules")

import os.path, re, httplib, urllib2, distutils.version, glob, tarfile, fchksum, shutil, time, commands, stat

version='2006.09.26'
inpath= ''
logDir = 'var/log/captracks'
capdir = 'usr/src/capsules'
pacodir='/var/log/paco'
trackCommand='make install'
confFile="/etc/cappy.conf"

preInstall='preInstall.sh'
postInstall='postInstall.sh'
preRemove = 'preRemove.sh'
postRemove = 'postRemove.sh'

errors=[]
options={
	'verboseExtract': '0',
	'stripOnCompress': '1',
	'verboseCompress': '1',
	'compressionType': 'bz2',
	'showCompressSummary': '1',
	'verboseRemove':'1',
	'verboseUpgrade':'1',
	'architecture':'armv5tej',
	'removeAfterInstall':'0',
	'progressExtract':'1'
}

def checkCapDir():
	if os.path.isdir("%s/%s" % (inpath,capdir)) == False:
		os.makedirs("%s/%s" % (inpath,capdir),0771)

if os.path.isfile(confFile):
	f=open(confFile, 'r')
	regex = re.compile("(^[^#].+)=(.+)$")
	
	for line in f:
		match = regex.match(line)
		if match:
			options[match.group(1)]=match.group(2)

class confFileError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return self.value

if options['architecture'].strip() == '':
	raise confFileError("Missing required conf argument 'architecture'")

class capsuleDatabaseError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return self.value
		
class capsuleDependencyError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return self.value

class capsuleGetcapError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return self.value

class ui:
	"""
		The interface to the frontend, whatever code utilizes this module should follow this interface.
		This class will allow the backend, to communicate progress/data to the frontend.
		The default frontend provided here, is a command line front end.
	"""
	def printError(self, message):
		print "\n - Capsules system encountered a fatal error '"+str(message)+"'\n"
	
	def printCapEntry(self,cap):
		"""
			Print the contents of a capEntry object
		"""
		if cap == None:
			errors.append("Capsule not found in database")
			return True
		print """
  Keyword: %s
  Capname: %s
  Deps:
	%s
  md5sum: %s
  architecture: %s
  version: %s
  description:
	%s
  urls:
   %s
""" % (cap.keyword,cap.capname,"\n\t".join(cap.deps),cap.md5,cap.arch,cap.version,cap.desc,"\n   ".join(cap.urls))
		return True
	
	def installCap(self,cap,status,output):
		"""
			Prints information during an install of a capsule.
			status codes:
				status -1 = install failed
				status 0 = starting install
				status 0.5 = running pre install scripts
				status 0.9 = pre install scripts finished
				status -0.5 = pre install scripts failed
				status 1 = extracting
				status 2 = running post install scripts
				status 2.1 =  print output of scripts
				status 2.5 = post install scripts finished
				status -2 = post install scripts failed
				status 3 = finished installing
		"""
		if status == 0:
			print "\n Installing '"+cap.capname+"'"
			print " >#####################################################\n"
		elif status == 0.5:
			print " Running pre install scripts"
			print " >---------------------------------------------------\n"
		elif status == 0.9:
			print "\n ---------------------------------------------------<"
			print " ++ pre install scripts succeeded\n"
		elif status == -0.5:
			print ">>> Pre install scripts failed"
		elif status == 2:
			print " Running post install scripts"
			print " >---------------------------------------------------\n"
		elif status == 2.1:
			print output
		elif status == 2.5:
			print "\n ---------------------------------------------------<"
			print " ++ post install scripts succeeded\n"
		elif status == 3:
			print "\n #####################################################<"
			print " ++ install complete\n"
		elif status == -2:
			print ">>> Post install scripts failed"
		elif status == -1:
			print "\n #####################################################<\n"
			print "\n>>> INSTALL FAILED\n"
		return True
		
	def removeCap(self,cap,status,output):
		"""
			Prints information during a removal of a capsule.
			status codes:
				status -1 = removal failed
				status 0 = starting removal
				status 1 = executing pre-removal script
				status 1.1 = print output from script
				status 2 = removing files
				status 3 = cleaning up empty directories
				status 4 = executing post remove script
				status 5 = removing captrack
				status 6 = removal finished
		"""
		if status == 0:
			print "\n Removing '"+cap.capname+"'"
			print " >#####################################################\n"
		if status == -1:
			print "\n #####################################################<\n"
			print "\n>>> REMOVAL FAILED\n"
		if status == 1:
			print " > Running pre-removal scripts"
		if status == 1.1:
			print output
		if status == 2:
			print " > Removing files"
			#print " >---------------------------------------------------\n"
		if status == 3:
			#print "\n ---------------------------------------------------<\n"
			print " > Cleaning up directories"
		if status == 4:
			print " > Running post-remove scripts"
		if status == 5:
			print " > Removing captrack"
		if status == 6:
			print "\n #####################################################<"
			print " ++ removal complete\n"
		return True
	
	def getCap(self,cap,status):
		"""
			Prints information about what getcap is doing
			status codes:
				status 0 = starting getcap
				status 1 = resolving dependencies
				status 1.5 = dep resolution complete
				status 3 = checking integrity
				status 4 = installing
				status 5 = finished
		"""
		if status == 1:
			print " Resolving dependencies"
			print " >#####################################################\n"
		elif status == 1.5:
			print "\n #####################################################<\n"
		elif status==5:
			print " > Getcap finished\n"
		return True
	
	def capTrackDestDir(self, capname, status):
		"""
			Prints progress about what capTrackDestDir is doing.
			status codes:
				status -1 = failed
				status 0 = starting captrack
				status 1 = discovering file structure
				status 2 = building filelist
				status 4 = guessing deps
				status 4 = finished/succeeded
		"""
		if status == -1:
			print " -- Captrack failed"
		elif status == 0:
			print " > Starting captrack for '%s'" % capname
		elif status == 1:
			print "\tDiscovering file Structure"
		elif status ==2:
			print "\tBuilding file list"
		elif status ==3:
			print "\tGuessing deps"
		elif status ==4:
			print " < Captrack finished"
		return True	
	
	def capTrack(self, capname, cmd, status):
		"""
			Prints progress about what capTrack is doing.
			status codes:
				status -1 = failed
				status 0 = starting captrack
				status 1 = starting paco
				status 2 = paco failed
				status 3 = finished/succeeded
		"""
		if status == -1:
			print " -- Captrack failed"
		elif status == 0:
			print " > Starting captrack"
		elif status == 1:
			print "\trunning paco"
		elif status ==2:
			print "\t -- paco failed"
		elif status ==3:
			print " < Captrack finished"
		return True
	
	def capTrackListAll(self,listItem, status):
		"""
			List all the capsules installed.
			status codes:
				status -1 = failed
				status 0 = starting
				status 1 = passing listItem
				status 2 = finished
		"""
		if status == -1:
			print " -- Could not get capsule list"
		elif status == 0:
			print " > Capsule list"
		elif status == 1:
			print "\t"+listItem
		elif status == 2:
			print " < Finished"
		return True
	
	def capTrackList(self, capname, listItem, status):
		"""
			List the files inside a captrack filelist.
			status codes:
				status -1 = failed
				status -2 = doesn't exist
				status 0 = starting
				status 1 = passing a listItem
				status 2 = finished
		"""
		if status == -2:
			print " -- Capname '%s' doesn't exist" % capname
		elif status == -1:
			print " -- Cannot get list for '%s'" % capname
		elif status ==0:
			print " > List for '%s'" % capname
		elif status == 1:
			print listItem
		elif status == 2:
			print " < Finished"
		return True
	
	def capBuild(self, status, counter, max):
		"""
			Prints information about what capBuild is doing.
			status codes:
				status -1 = failed
				status 0 = starting buildcap
				status 1 = initializing folders and file list
				status 2 = copying capsule script files
				status 3 = building capsule
				status 4 = closing capsule
				status 5 = finished
		"""
		if status == 1:
			print "\n Initializing capsule folders"
		elif status == 2:
			print "\n Building Capsule"
			sys.stdout.write("  completed % 00")
			sys.stdout.flush()
		elif status == 3 and counter != None and max != None:
			sys.stdout.write("\b\b\b")
			sys.stdout.write( "%03i" % int(100.00*((counter*1.00)/(max*1.00))))
			sys.stdout.flush()
		elif status == -1:
			print "\n capBuild failed!"
		elif status == 4:
			print "\n\n   > Adding the final touches..."
		elif status == 5:
			print "\n  [Finished]\n"
		return True
	
	def capCompress(self, status, cap):
		"""
			Prints information about what capCompress is doing in the backend.
			status codes:
				status -1 = failed
				status 0 = starting up
				status 1 = stripping debug info
				status 2 = compressing
				status 3 = finished
				status 4 = creating summary
				status 5 = print summary
		"""
		if status == 0:
			print "\n  Starting capsule compression function"
		elif status == 1:
			print "   Stripping binary debug data"
		elif status == 2:
			print "   Compressing capsule into .cap"
		elif status == 3:
			print "  [Finished]"
		elif status == 4:
			print "\n Creating summary..."
		elif status == 5:
			print """
@ Summary @-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-

	Capname = """+cap.capname+"""
	MD5 = """+fchksum.fmd5t(cap.capname+".cap")[0]+"""

@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@
"""
		elif status == -1:
			print "\n compression FAILED \n"
		return True
	
	def capCompressFile(self, fname):
		"""
			Displays the name of the file being extracted.
		"""
		print "\t"+fname
		return True
	
	def dependencies(self,deps):
		"""
			Called when a deplist is retrieved for a capsule.
		"""
		return True
		
	def capDep(self,cap):
		"""
			Called when a capsule is about to be retrieved and installed, to fulfill a dependency for another capsule.
		"""
		if cap != None:
			print " # Resolving dependency '"+cap.capname+"'\n"
		return True
	
	def downloadProgress(self,progress, status, filename, size):
		if status == 0:
			sys.stdout.write("  Downloading file: '"+filename+"' "+str(size)+"\n\t% 000")
			sys.stdout.flush()
		elif status==1:
			if progress > 0:
				sys.stdout.write("\b\b\b")
				sys.stdout.write( "%03i" % progress)
				sys.stdout.flush()
		elif status==2:
			sys.stdout.write("\b\b\b")
			print "100"
			print "   [Finished]\n"
			sys.stdout.flush()
		elif status==-1:
			print "\n  Download failed!"
			sys.stdout.flush()
		return True

	def integrity(self,cap,status):
		"""
			Called before and after an integrity check.
			Reports when the integrity checks starts, and the status after it completes.
			status codes:
				status 0 = integrity check started
				status 1 = passed
				status -1 = failed
		"""
		if status == 0:
			print "  Checking integrity"
		elif status == 1:
			print "   [Passed]\n"
		elif status == -1:
			print "   [Failed]\n"
			sys.exit(1)
		return True
		
	progress=0
	def extract(self, cap, status, curFileName,numFiles,currentFileNum,isCount):
		"""
			Called before, and during a capsule decompression/extraction.
			Reports back progress.
			status codes:
				status 0 = starting extraction of cap
				status 1 = extracting cap
				status 2 = capsule extraction finished
				status 3 = print filename of current extraction
		"""
		if status == 0:
			sys.stdout.write("  Extracting capsule\n\t%s %i" % (('%','file count:')[isCount],self.progress));sys.stdout.flush()
			self.progress=0
		elif status == -1:
			print "\n"+curFileName+"\n"
			self.progress=0
			sys.exit(1)
		elif status == 2:
			sys.stdout.write( "\b" * len(str("%i" % self.progress)) ); sys.stdout.flush()
			print (100,self.progress)[isCount]
			print "   [Finished]\n"
			self.progress=0
		elif status ==1:
			sys.stdout.write( "\b" * len(str("%i" % self.progress)) ); sys.stdout.flush()
			if isCount:
				self.progress=currentFileNum
			else:
				self.progress=((1.00*currentFileNum)/numFiles)*100
			sys.stdout.write( "%i" % int(self.progress) ); sys.stdout.flush()
		elif status ==3:
			print "\t"+curFileName
		else:
			return False
		return True
		
	def upgrade(self,cap,status):
		"""
			Called before and during an upgrade of a capsule.
			status codes:
				status -1 = failed upgrade
				status 0 = starting an upgrade
				status 1 = install local capsule
				status 2 = install capsule from repo
				status 3 = capsule already latest
				status 4 = capsule not installed on local system
				status 5 = installing capsule, from either source
				status 6 = removing remaining old files, that weren't overwritten
				status 7 = cleaning up old empty directories
				status 8 = removing old captrack file
				status 9 = finished an upgrade
		"""
		if status == 0:
			print "  Starting upgrade: '"+cap.capname+"'"
		elif status ==-1:
			print " --- Upgrade Failed"
		elif status ==1:
			print " > Using Local capsule"
		elif status ==2:
			print " > Using repository"
		elif status ==3:
			print " -- Capsule '%s' already latest" % cap.capname
		elif status ==4:
			print "Capsule '%s' hasn't been installed on the system" % cap.capname
		elif status ==5:
			print " > Installing capsule"
		elif status ==6:
			print " > Removing left over files"
		elif status ==7:
			print " > Cleaning up empty directories"
		elif status ==8:
			print " > Removing old captrack file"
		elif status ==9:
			print "\t[Finished]\n"
		return True

class capEntry:
	"""
		Holds a capsule entry, info like keyword, capname, etc from the capsule database
	"""
	keyword=''
	capname=''
	deps=[]
	urls=[]
	desc=''
	md5=''
	version=''
	
	numDBFields=7
	fieldSeparator='%'
	listSeparator=','
	
	def parse(self,data):
		"""
			Parse a delimited string into the data structure of this class
		"""
		
		fieldNum = len(data.split(self.fieldSeparator))-1
		if fieldNum != self.numDBFields:
			raise capsuleDatabaseError("Format mismatch '"+str(fieldNum)+" : "+data)
		
		self.keyword,self.capname,deps,self.md5,urls,self.desc,self.arch,self.version = data.strip().split(self.fieldSeparator)
		
		self.deps=deps.split(self.listSeparator)
		self.urls=urls.split(self.listSeparator)
	
	def __str__(self):
		"""
			Return the delimited string this class represents
		"""
		return self.fieldSeparator.join(
			[
				self.keyword,
				self.capname,
				self.listSeparator.join(self.deps),
				self.md5,
				self.listSeparator.join(self.urls),
				self.desc,
				self.arch,
				self.version
			])

class capDatabaseInterface:
	"""
		Defines the common interface for all database access classes
	"""
	dbloc = ''
	dbalias=''
	depalias=''
	dbtype=''
	defaults=['stable','test','submit']
	
	def getEntry(self,keyword, alias):
		"""
			Returns a capEntry object from the database
			Returns None when the database entry is not found
		"""
		return None
		
	def getSearch(self,term, alias):
		"""
			Returns a list of capEntry objects returned from the search for the <term>, [<#Results>,[<capEntryobjs>]]
			Returns None when there are no search results
		"""
		return [0,[None]]
		
	def update(self, alias):
		"""
			Returns the url and file location to download the database to
			[<url>,<file>]
		"""
		return []

class capFlatFile(capDatabaseInterface):
	"""
		Access the flatfile version of the capsule database
	"""
	dbloc = 'usr/src/capsules/capsuledb'
	dbalias='stable'
	depalias=''
	dbtype='flatfile'
	
	dbCommonUrl='http://caps.neurosaudio.com/lists/capsuledb'
	
	def getEntry(self,keyword, alias):
		"""
			Returns a capEntry object from the database
			Returns None when the database entry is not found
		"""
		dbfilename = inpath+"/"+self.dbloc+"."+alias
		dbFile=None
		if os.path.exists(dbfilename) == False:
			raise capsuleDatabaseError("Cannot find database: "+inpath+"/"+self.dbloc+"."+alias)
		
		cap = capEntry()
		if keyword == None or keyword.strip() == '':
			raise capsuleDatabaseError("Missing keyword")
		
		try:
			dbFile = open(dbfilename, 'r')
		except:
			raise capsuleDatabaseError("Cannot open database: "+inpath+"/"+self.dbloc+"."+alias)
		
		dbentry=None
		for line in db:
			if line.split("%")[0].strip() == keyword:
				dbentry=line.strip()
		
		dbFile.close()
		
		if dbentry == None:
			return None
		
		cap.parse(dbentry)
		return cap
		
	def getSearch(self,term, alias):
		"""
			Returns a list of capEntry objects returned from the search for the <term>, [<#Results>,[<capEntryobjs>]]
			Returns None when there are no search results
		"""
		return [0,[None]]
		
	def update(self, alias):
		"""
			Returns the url and file location to download the database to
			[<url>,<file>]
		"""
		return [inpath+"/"+self.dbloc+"."+alias, dbCommonUrl+"."+alias]

		
class capWeb(capDatabaseInterface):
	"""
		Represents a connection to the webservice that provides capsule entries.
		It will connect and retrieve single entries or search queries using a database alias
	"""
	dbloc = 'caps.neurosaudio.com'
	dbalias='stable'
	depalias='stable'
	dbtype='capWeb'
	
	def _getData(self,term,alias,type):
		connection=None
		response=None
		data=None
		
		if alias == None:
			alias = self.dbalias
		
		if term == None or term == '':
			raise capsuleDatabaseError("Missing term")
			
		try:
			connection = httplib.HTTPConnection(self.dbloc)
		except KeyboardInterrupt:
			sys.exit(1)
		except:
			raise capsuleDatabaseError("Cannot open database: "+self.dbtype)
			
		try:
			connection.request("GET", "/capbackend.php?type="+type+"&data="+term+"&alias="+alias+"&arch="+options['architecture'])
			response = connection.getresponse()
			if ( response.status != 200 ):
				raise capsuleDatabaseError("Database Read error: "+self.dbtype)
			data = response.read()
		except KeyboardInterrupt:
			sys.exit(1)
		except:
			connection.close()
			raise capsuleDatabaseError("Database Read error: "+self.dbtype)
		
		connection.close()
		
		if data.strip() == "none":
			return None
		
		return data
	
	def getEntry(self,keyword, alias):
		data = self._getData(keyword,alias,"get")
		if data == None:
			return None
		
		cap = capEntry()
		cap.parse(data)
		return cap
		
	def getSearch(self,term,alias):
		data = self._getData(term,alias,"search")
		
		if data == None:
			return []
		
		caps=[]
		
		for line in data.split("\n"):
			if line != '':
				cap = capEntry()
				cap.parse(line)
				caps.append(cap)
		
		return caps


####################################################################
# capsule dependency functions

def getVersion(keyword):
	"""
		Return the first version found of the capsule installed on the system, specified by keyword
	"""
	r = re.compile("^.*-(.*)$")
	for file in glob.glob("%s/%s/%s-*" % (inpath, logDir, keyword)):
		m = r.match(os.path.basename(file))
		if m:
			return [m.group(1),os.path.basename(file)]
	return None
	
def getVersions(keyword):
	"""
		Return the versions of the capsules installed on the system, specified by keyword
	"""
	files=[]
	r = re.compile("^.*-(.*)$")
	for file in glob.glob("%s/%s/%s-*" % (inpath, logDir, keyword)):
		m = r.match(os.path.basename(file))
		if m:
			files.append(m.group(1))
	return files

def separateDep(dep):
	"""
		Separates a dep out into [keyword,operator,version]
	"""
	r = re.compile("^(.*)\((..)(.*)\)$")
	m = r.match(dep.strip())
	if m:
		return [m.group(1),m.group(2),m.group(3)]
	return [dep, None, None]


def compareVersions(version1, operator, version2):
	"""
		Compare version1 to version2 using a valid capdep operator.
		Returns 1 if it evaluates to true, 0 if it is false
	"""
	version1=distutils.version.LooseVersion(version1)
	version2=distutils.version.LooseVersion(version2)
	
	eval=version1.__cmp__(version2)
	
	if operator == "==":
		if eval == 0:
			return 1
		return 0
	elif operator == ">=":
		if eval >=0:
			return 1
		return 0
	elif operator == "<=":
		if eval <= 0:
			return 1
		return 0
	elif operator == "<<":
		if eval < 0:
			return 1
		return 0
	elif operator == ">>":
		if eval > 0:
			return 1
		return 0
	
	raise capsuleDependencyError("Invalid Operator")

def evaluateDepCheck(depString):
	"""
		Takes depstring like 'xchat(>=2.4.4)', and evaluates it with the local system
	"""
	r = re.compile("^.*-(.*)$")
	keyword,operator,version=separateDep(depString)
	if operator == None:
		for file in glob.glob("%s/%s/%s-*" % (inpath, logDir, keyword)):
			return 1
		return None
	else:
		count = 0
		for file in glob.glob("%s/%s/%s-*" % (inpath, logDir, keyword)):
			file=os.path.basename(file)
			m = r.match(file)
			if m:
				if compareVersions(m.group(1), operator, version):
					return 1
				count+=1
		if count > 0:
			return 0
		else:
			return None	

####################################################################

def removeEntireDir(dir):
	"""
	     Recursively removes directories
	"""
	if os.path.isdir(dir):
		for root, dirs, files in os.walk(dir, topdown=False):
			for name in files:
				os.remove(os.path.join(root, name))
			for name in dirs:
				os.rmdir(os.path.join(root, name))
		os.rmdir(dir)

def depthSort(x,y):
	"""
	     Used to compare strings, the one with the most '/' will be ordered first.
	     This is used to sort a list of directories, so we can run rmdir 
	     on them from the deepest folder out.
	"""
	return  y.count('/') - x.count('/')

def seperateCap(capname):
	"""
		Returns [<keyword>,<version>]
		from properly named capnames
	"""
	capname = os.path.basename(capname.strip().replace(".cap",''))
	r = re.compile("^(.+)-(.+)$")
	m = r.match(capname)
	if m:
		return [m.group(1),m.group(2),capname]
	return [None,None,None]

def integrity(cap):
	"""
		Check a capsule's integrity using the md5sum stored in the capentry
	"""
	try:
		if cap.md5 == fchksum.fmd5t(cap.capname+".cap")[0]:
			return True
		return False
	except KeyboardInterrupt:
		sys.exit(1)
	except:
		return False

def capExtractSingle(cap, path, ui, filePath):
	"""
		Extract a file from capsule to path.
		Returns [bool, <errorMessage>], can return None object as message if no message is needed.
	"""
	tar = None
	try:
		tar = tarfile.open(cap.capname+".cap", "r:bz2")
	except tarfile.ReadError:
		tar = tarfile.open(cap.capname+".cap", "r:gz")
	
	try:
		fileInfo=tar.getmember(filePath)
		curName = inpath+"/"+fileInfo.name
		if os.path.isdir(curName) == False or tarinfo.isdir() ==False:
			
			tar.extract(fileInfo, inpath+"/")
			
			if not ui.extract(cap,3,curName, 1, 1,True):
				return False
				
			try:
				tar.chown(fileInfo,curName)
				tar.chmod(fileInfo,curName)
			except KeyboardInterrupt:
				sys.exit(1)
			except:
				curName=''
				
	except Exception, e:
		tar.close()
		return False

	tar.close()
	
	return True

def capExtract(cap, path, ui):
	"""
		Extract a capsule to path.
		Extracts cap to path,  if verboseExtract is set in options,
		this function will call ui.extract(cap,status,currentFileName, numFiles, currentCount)
	"""
	tar = None
	try:
		tar = tarfile.open(cap.capname+".cap", "r:bz2")
	except tarfile.ReadError:
		tar = tarfile.open(cap.capname+".cap", "r:gz")
		
	count=0
	max=0
	curName = ''
	
	getLineCount=0
	if options.has_key('progressExtract'):
		try:
			getLineCount=int(options['progressExtract'])
		except:
			getLineCount=0
	
	vExtract = 0
	if options.has_key('verboseExtract'):
		try:
			vExtract=int(options['verboseExtract'])
		except:
			vExtract=0
	
	if not ui.extract(cap,0,'',0,0,(getLineCount !=True)):
		return False
	
	try:
		if getLineCount:
			max=len(tar.getnames())
		
		for tarinfo in tar:
			curName = inpath+"/"+tarinfo.name
			if os.path.isdir(curName) == False or tarinfo.isdir() ==False:
				
				tar.extract(tarinfo, inpath+"/")
				if not ui.extract(cap,(1,3)[vExtract],curName, max, count,getLineCount !=True):
					return False
					
				try:
					tar.chown(tarinfo,curName)
					tar.chmod(tarinfo,curName)
				except KeyboardInterrupt:
					sys.exit(1)
				except:
					curName=''
				count+=1
	except Exception, e:
		tar.close()
		ui.extract(cap,-1,"Failed Extraction: "+str(e),-1,-1,getLineCount !=True)
		return False
	
	tar.close()
	if not ui.extract(cap, 2,'',count,count,getLineCount !=True):
		return False
	
	return True


def download(cap,ui):
	"""
		Downloads cap, it will try all the urls untill one works.
	"""
	
	if os.path.exists(cap.capname+".cap"):
		if integrity(cap):
			return True
		else:
			os.remove(cap.capname+".cap")
	
	chunkSize=1024*8
	succeeded=False
	for url in cap.urls:
		try:
			opener = urllib2.build_opener()
			opener.addheaders = [('User-agent', 'cap-python/'+version)]
			u=opener.open(url)
			totalSize = float(u.headers['content-length'])
			
			sizeString = "%.2f Bytes" % (totalSize * 1.00)
			if totalSize > 1024:
				sizeString = "%.2f Kb" % (totalSize/1024.00)
			if totalSize > (1048576):
				sizeString = "%.2f Mb" % (totalSize/1048576.00)
			if totalSize > (1073741824):
				sizeString = "%.2f Gb ahh!" % (totalSize/1073741824.00)
			
			if not ui.downloadProgress(0, 0, cap.capname+'.cap',sizeString):
				return False
			
			if totalSize > 0:
				f=open(cap.capname+".cap",'w')
				current=0
				
				while current <= totalSize:
					f.write(u.read(chunkSize))
					if not ui.downloadProgress(int(100.00*(current/totalSize)), 1, file,totalSize):
						return False
					current+=chunkSize
				if not ui.downloadProgress(0, 2, file,totalSize):
					return False
				
				f.close()
			
			u.close()
			succeeded=True
			break
			
		except urllib2.HTTPError, e:
			if not ui.downloadProgress(0, -1, cap.capname,0):
				return False
			if e.code == 404:
				errors.append("download error: '"+url+"' not found ( 404 ).")
			elif e.code == 403:
				errors.append("download error: Cannot access '"+url+"', permission denied ( 403 )")
			else:
				errors.append("urlib2 error: "+e.code)
		
	return succeeded

def resolveDeps(cap):
	"""
		Returns three lists of dependencies:
			foundDeps 		-- deps from cap, that are found on thesystem
			notFoundDeps	-- deps that are not found on the system
			upgradeDeps	-- deps that need to be upgraded
	"""
	foundDeps = []
	notFoundDeps = []
	upgradeDeps  = []
	for dep in cap.deps:
		isDep = evaluateDepCheck(dep)
		if isDep == 1:
			foundDeps.append(dep)
		elif isDep == 0:
			upgradeDeps.append(dep)
		elif isDep == None:
			notFoundDeps.append(dep)
	
	return [foundDeps,notFoundDeps,upgradeDeps]

def installCap(cap, ui):
	"""
		Install a capsule, report back progress to ui.
	"""
	if not ui.installCap(cap, 0,None):
		return False
	if capExtractSingle(cap,inpath, ui, "var/log/captracks/%s/%s" % (cap.capname, preInstall)):
		if not ui.installCap(cap, 0.5,None):
			return False
		status,output=commands.getstatusoutput("inpath=%s capName=%s logDir=%s sh %s/%s/%s/%s" % (inpath,cap.capname, logDir, inpath,logDir,cap.capname,preInstall) )
		ui.installCap(cap,2.1,output)
		if status != 0:
			errors.append("Pre install scripts failed for '"+cap.capname+"'")
			ui.installCap(cap, -0.5,None)
			ui.installCap(cap, -1,None)
			return False
		
		if not ui.installCap(cap, 0.9,None):
			return False
	
	if not ui.installCap(cap, 1,None):
		return False
	if capExtract(cap, inpath+"/",ui) == False:
		errors.append("extraction failed for '"+cap.capname+"'")
		ui.installCap(cap, -1,None)
		return False
	
	cap.capname = os.path.basename(cap.capname)
	
	if os.path.exists("%s/%s/%s/%s" % (inpath, logDir, cap.capname,postInstall)):
		if not ui.installCap(cap, 2,None):
			return False
		status,output=commands.getstatusoutput("inpath=%s capName=%s logDir=%s sh %s/%s/%s/%s" % (inpath,cap.capname, logDir, inpath,logDir,cap.capname,postInstall) )
		ui.installCap(cap,2.1,output)
		if status != 0:
			errors.append("Post install scripts failed for '"+cap.capname+"'")
			ui.installCap(cap, -2,None)
			ui.installCap(cap, -1,None)
			return False
		
		if not ui.installCap(cap, 2.5,None):
			return False
	
	if 'removeAfterInstall' in options.keys() and options['removeAfterInstall'] == '1':
		os.system("rm -f %s/%s/%s.cap" % (inpath,capdir,cap.capname))
	
	if os.path.isdir("%s/%s/%s" % (inpath, logDir, cap.capname)):
		if not ui.installCap(cap, 3,None):
			return False
		return True
	
	errors.append("Could not find %s/%s/%s" % (inpath, logDir, cap.capname))
	
	ui.installCap(cap, -1,None)
	return False

def removeCap(cap, ui):
		"""
			Remove an installed capsule, and all empty dirs associated with that capsule.
		"""
		
		if not ui.removeCap(cap,0,None):
			return False
		
		if os.path.isfile("%s/%s/%s/filelist" % (inpath,logDir,cap.capname)) == False:
			ui.removeCap(cap,-1,None)
			errors.append("Could not find '%s/%s/%s/filelist'" % (inpath,logDir,cap.capname))
			return False
			
		vRemove = False
		if 'verboseRemove' in options.keys():
			vRemove = True
		
		if os.path.exists("%s/%s/%s/%s" % (inpath,logDir,cap.capname,preRemove)):
			if not ui.removeCap(cap,1,None):
				return False
			status,output=commands.getstatusoutput("inpath=%s capName=%s logDir=%s sh %s/%s/%s/%s" % (inpath,cap.capname, logDir, inpath,logDir,cap.capname,preRemove) )
			ui.removeCap(cap,2.1,output,None)
			if status != 0:
				errors.append("Pre-Removal scripts failed for '"+cap.capname+"'")
				ui.removeCap(cap,-1,None)
				return False
		
		if not ui.removeCap(cap,2,None):
			return False
		oldPkg=open("%s/%s/%s/filelist" % (inpath,logDir,cap.capname), 'r')
		
		dirs=[]
		for line in oldPkg:
			line = line.strip()
			
			folder = os.path.dirname(inpath+"/"+line)
			if os.path.isdir(inpath+"/"+line):
				folder = (inpath+"/"+line).rstrip("/")
			
			if folder in dirs == False:
				dirs.append(folder)
				
			try:
				os.remove(line)
			except KeyboardInterrupt:
				sys.exit(1)
			except:
				errors.append("Failed to remove '"+line+"'")
			
		oldPkg.close()
		
		if not ui.removeCap(cap,3,None):
			return False
		# remove leftover directories
		
		for dir in dirs: # get all the directories above all the directories in dirs
			cur = dir
			while cur != '/' and cur != '.':
				if os.path.dirname(cur) in dirs == False:
					dirs.append(os.path.dirname(cur))
					cur=os.path.dirname(cur)
		
		dirs.sort(depthSort) # sort them so the deepest dirs come up first
		
		for dir in dirs:
			if dir != '/':
				try:
					os.rmdir(dir)
				except KeyboardInterrupt:
					sys.exit(1)
				except:
					dir='/'
		
		if os.path.exists("%s/%s/%s/%s" % (inpath,logDir,cap.capname,postRemove)):
			if not ui.removeCap(cap,4,None):
				return False
			status,output=commands.getstatusoutput("inpath=%s capName=%s logDir=%s sh %s/%s/%s/%s" % (inpath,cap.capname, logDir, inpath,logDir,cap.capname,postRemove) )
			ui.removeCap(cap,2.1,output)
			if status != 0:
				errors.append("Post-Removal scripts failed for '"+cap.capname+"'")
				ui.removeCap(cap,-1,None)
				return False
		
		if not ui.removeCap(cap,5,None):
			return False
		try:
			removeEntireDir(inpath+"/"+logDir+"/"+cap.capname)
		except KeyboardInterrupt:
			sys.exit(1)
		except:
			errors.append("Could not remove '%s'" % (inpath+"/"+logDir+"/"+cap.capname))
			ui.removeCap(cap,-1,None)
			return False
		
		
		if not ui.removeCap(cap,6,None):
			return False
		
		return True

def upgradeCap(cap, db, ui, localCapName, isDep):
	"""
		Upgrade a capsule to either a local capsule specified by localCapName,
		if localCapName is None upgrade will try and retrieve latest from the repository
		if the local installed version is not the latest.
	"""
	isSync=False
	oldCap=None
	newCap=None
	oldCapTrackFile=''
	
	if not ui.upgrade(cap, 0):
		return False
	
	if localCapName != None:
		if not ui.upgrade(cap, 1):
			return False
		
		capLocalDir = os.path.dirname(localCapName)
		if os.path.exists(localCapName) == False:
			errors.append("Cannot locate '"+localCapName+"'")
			ui.upgrade(cap, -1)
			return False
		oldKey,oldVersion,oldCapName = seperateCap(cap.capname)
		if oldKey == None or oldKey == '':
			errors.append("Requires capname as argument for upgrade")
			return False
		oldCap=capEntry()
		oldCap.capname=oldCapName
		oldCap.keyword=oldKey
		oldCap.version=oldVersion
		
		newKey,newVersion,newCapName = seperateCap(localCapName)
		if newKey == None or newKey == '':
			errors.append("Requires capname as argument for upgrade")
			return False
		newCap=capEntry()
		newCap.capname=newCapName
		newCap.keyword=newKey
		newCap.version=newVersion
		
		oldCapTrackFile = "%s/%s/%s/filelist" % (inpath,logDir,oldCap.capname)
		if os.path.exists(oldCapTrackFile) == False:
			errors.append("Cannot locate '"+oldCapTrackFile+"'")
			ui.upgrade(cap, -1)
			return False
		
		if oldCap.capname == newCap.capname:
			isSync=True
			newoldCapTrackFile = inpath+'/capCleanSync.'+oldCap.capname
			try:
				shutil.copy2(oldCapTrackFile,newoldCapTrackFile)
			except KeyboardInterrupt:
				sys.exit(1)
			except:
				errors.append("Clean sync failed to relocate '%s' to '%s'"% (oldCapTrackFile,newoldCapTrackFile))
				ui.upgrade(cap, -1)
				return False
			oldCapTrackFile=newoldCapTrackFile
			
		if not ui.upgrade(cap, 5):
			return False
		if installCap(newCap, ui) == False:
			errors.append("upgrade failed to install capsule")
			ui.upgrade(cap, -1)
			return False
	else:
		if not ui.upgrade(cap, 2):
			return False
		olddir=os.getcwd()
		os.chdir(inpath+"/"+capdir)
		
		oldKey,oldVersion,oldCapName = seperateCap(cap.capname)
		if oldKey == None or oldKey == '':
			errors.append("Requires capname as argument for upgrade")
			return False
		oldCap=capEntry()
		oldCap.capname=oldCapName
		oldCap.keyword=oldKey
		oldCap.version=oldVersion
		
		newCap = None
		
		if isDep:
			newCap=db.getEntry(oldCap.keyword,db.depalias)
		else:
			newCap=db.getEntry(oldCap.keyword,db.alias)
			
		if newCap == None:
			errors.append("Cannot find '%s' in database with alias '%s'" % (oldCap.keyword, db.alias))
			ui.upgrade(cap, -1)
			return False
			
		indep=evaluateDepCheck("%s(<<%s)" % (newCap.keyword,newCap.version))
		if indep == None:
			errors.append("Cannot find '"+oldCap.capname+"' on local system, so cannot upgrade it")
			ui.upgrade(cap, 4)
			ui.upgrade(cap, -1)
			return False
		elif indep == 0:
			ui.upgrade(cap, 3)
			return True
		
		oldCapTrackFile = "%s/%s/%s/filelist" % (inpath,logDir,oldCap.capname)
		if os.path.exists(oldCapTrackFile) == False:
			errors.append("Cannot locate '"+oldCapTrackFile+"'")
			ui.upgrade(cap, -1)
			return False
		
		if oldCap.capname == newCap.capname:
			isSync=True
			newoldCapTrackFile = inpath+'/capCleanSync.'+oldCap.capname
			try:
				shutil.copy2(oldCapTrackFile,newoldCapTrackFile)
			except KeyboardInterrupt:
				sys.exit(1)
			except:
				errors.append("Clean sync failed to relocate '%s' to '%s'"% (oldCapTrackFile,newoldCapTrackFile))
				ui.upgrade(cap, -1)
				return False
			oldCapTrackFile=newoldCapTrackFile
		
		if not ui.upgrade(cap, 5):
			return False
		if getCap(newCap,db,ui) == False:
			errors.append("Upgrade failed on getcap")
			ui.upgrade(cap, -1)
			return False
			
		if os.path.exists(newCap.capname+".cap") == False:
			errors.append("Cannot find capsule '"+newCap.capname+".cap' after getcap")
			ui.upgrade(cap, -1)
			return False
	
	if not ui.upgrade(cap, 6):
		return False
	oldPkg=open(oldCapTrackFile, 'r')
	
	upgradeVerbose = False
	if 'upgradeVerbose' in options.keys() and options['upgradeVerbose'] == '1':
		upgradeVerbose = True
	
	dirs=[]
	for line in oldPkg:
		line = line.strip()
		if commands.getoutput("grep -e \"^%s\$\" \"%s\"" % (line,inpath+"/"+logDir+"/"+newCap.capname+"/filelist")) == '':
			folder = os.path.dirname(inpath+"/"+line)
			if os.path.isdir(inpath+"/"+line):
				folder = (inpath+"/"+line).rstrip("/")
			
			if folder in dirs == False:
				dirs.append(folder)
			
			if upgradeVerbose:
				print "Removing '"+line+"'"
				
			try:
				os.remove(line)
			except KeyboardInterrupt:
				sys.exit(1)
			except:
				errors.append("Failed to remove '"+line+"'")
	oldPkg.close()
	
	if not ui.upgrade(cap, 7):
		return False
	# remove leftover directories
	
	for dir in dirs: # get all the directories above all the directories in dirs
		cur = dir
		while cur != '/' and cur != '.':
			if os.path.dirname(cur) in dirs == False:
				dirs.append(os.path.dirname(cur))
				cur=os.path.dirname(cur)
	
	dirs.sort(depthSort) # sort them so the deepest dirs come up first
	
	for dir in dirs:
		if dir != '/':
			try:
				os.rmdir(dir)
			except KeyboardInterrupt:
				sys.exit(1)
			except:
				dir='/'
	
	if not ui.upgrade(cap, 8):
		return False
	# remove the oldcaptrack file
	if isSync == False:
		if os.path.exists(inpath+"/"+logDir+"/"+oldCap.capname):
			try:
				removeEntireDir(inpath+"/"+logDir+"/"+oldCap.capname)
			except KeyboardInterrupt:
				sys.exit(1)
			except:
				errors.append("Could not remove '%s'" % (inpath+"/"+logDir+"/"+oldCap.capname))
	else:
		try:
			os.remove(oldCapTrackFile)
		except KeyboardInterrupt:
			sys.exit(1)
		except:
			errors.append("Could not remove '%s'" % (oldCapTrackFile))
	
	if not ui.upgrade(cap, 9):
		return False
	return True

def getCap(cap, db, ui):
	"""
		Retrieves and installs a capsule.
		db = the database object in use
		reports back status info to the ui
	"""
	checkCapDir()
	if cap == None:
		errors.append("Must provide a valid capentry object")
		if not ui.getCap(cap,-1):
			return False
	if not ui.getCap(cap,0):
		return False
	olddir=os.getcwd()
	
	deps=resolveDeps(cap)
	if len(deps[1]) >0 or len(deps[2]) > 0:
		if not ui.getCap(cap,1):
			return False
		ui.dependencies(deps)
		
		for dep in deps[1]:
			capdep = db.getEntry(separateDep(dep)[0],db.depalias)
			if capdep == None:
				errors.append("Cannot find dep '%s' in repo '%s'" % (dep, db.depalias))
				return False
			
			if not ui.capDep(capdep):
				return False
			if getCap(capdep, db, ui) == False:
				return False
		
		for dep in deps[2]:
			capdep = capEntry()
			oldKey = separateDep(dep)[0]
			oldVersion,oldCapName=getVersion(oldKey)
			capdep.keyword=oldKey
			capdep.capname=oldCapName
			capdep.version=oldVersion
			if capdep == None:
				errors.append("Cannot find dep '%s' in repo '%s'" % (dep, db.depalias))
				return False
			
			if not ui.capDep(capdep):
				return False
			
			if upgradeCap(capdep,db,ui,None,True) == False:
				return False
	
		if not ui.getCap(cap,1.5):
			return False
		
	os.chdir(inpath+"/"+capdir)
	
	if download(cap,ui) == False:
		return False
	
	if not ui.getCap(cap,3):
		return False
	if not ui.integrity(cap,0):
		return False
	if integrity(cap) == False:
		ui.integrity(cap,-1)
		return False
	if not ui.integrity(cap,1):
		return False
	
	
	ui.getCap(cap,4)
	if installCap(cap,ui) == False:
		return False
	
	if not ui.getCap(cap,5):
		return False
	os.chdir(olddir)
	return True

def guessDeps(capFilelistPath):
	"""
	     Try to guess dependencies by finding all binary elf files in the filelist,
		then running ldd on them, and matching the output of ldd to all captracks currently on the system.
	"""
	libRegs=[]
	if len(capFilelistPath) > 1:
		if os.path.isfile(capFilelistPath+"/filelist"):
			f=open(capFilelistPath+"/filelist",'r')
			for line in f:
				line=line.strip()
				if os.path.isfile(line):
					out=commands.getoutput("file %s" % line)
					if (out.find("ELF") > -1 and out.find("executable") > -1) or (out.find("LSB shared object") > -1):
						ldd=commands.getoutput("ldd %s" % line)
						for l in ldd.split("\n"):
							l=l.strip()
							lib=l.split("=>")[0].split(" ")[0].strip()
							if lib != '':
								libs=commands.getoutput("grep -l %s /%s/*/filelist" % (lib,logDir)).split("\n")
								if len(libs) > 0:
									for filelist in libs:
										if filelist != '':
											capname=os.path.basename(os.path.dirname(filelist))
											if capname not in libRegs:
												libRegs.append(capname)
			f.close()
			return [True,libRegs]
		else:
			errors.append("Invalid capname %s, cannot find %s/filelist" % (capname,capFilelistPath))
			return [False,[]]
	else:
		errors.append("Requires argument, capname to check dependencies on")
		return [False,[]]

def capTrackListAll(ui):
	"""
		List all the capsules installed.
	"""
	ui.capTrackListAll(None, 0)
	if os.path.isdir("%s/%s" % (inpath, logDir)) == False:
		ui.capTrackListAll(None, -1)
		errors.append("Cannot find '%s/%s'" % (inpath, logDir))
		return False
		
	for item in glob.glob("%s/%s/*" % (inpath, logDir)):
		ui.capTrackListAll(os.path.basename(item.strip()), 1)
		
	ui.capTrackListAll(None, 2)
	return True

def capTrackList(capname, ui):
	"""
		List the files inside a captrack filelist.
	"""
	if os.path.isfile("%s/%s/%s/filelist" % (inpath, logDir, capname)) == False:
		ui.capTrackList(capname, None, -2)
		ui.capTrackList(capname, None, -1)
		errors.append("Cannot find '%s/%s/%s/filelist'" % (inpath, logDir, capname))
		return False
	
	capTrackFile = open("%s/%s/%s/filelist" % (inpath, logDir, capname), 'r')
	ui.capTrackList(capname, None, 0)
	for line in capTrackFile:
		ui.capTrackList(capname, line.strip(), 1)
	capTrackFile.close()
	ui.capTrackList(capname, None, 2)
	
	return True

def capTrackDestDir(ui,doGuess):
	"""
		Create a captrack for a directory, usually built with destdir.
		Attempts to guess dependencies as well, stores into deps.guess
	"""
	capname=os.path.basename(os.getcwd())
	ui.capTrackDestDir(capname,0)
	
	ui.capTrackDestDir(capname,1)
	f=commands.getoutput("find ./").split("\n")
	foundFiles=False
	
	os.makedirs("var/log/captracks/"+capname)
	if os.path.isdir("var/log/captracks/"+capname) == False:
		errors.append("Could not create directory '%s'" % "var/log/captracks/"+capname)
		ui.capTrackDestDir(capname,-1)
		return False

	ui.capTrackDestDir(capname,2)
	filelist=None
	try:
		filelist=open("var/log/captracks/"+capname+'/filelist','w')
	except:
		errors.append("Could not create filelist '%s'" % "var/log/captracks/"+capname+'/filelist')
		ui.capTrackDestDir(capname,-1)
		return False
	
	for line in f:
		foundFiles=True
		newline=line.lstrip(".").strip()
		if newline != "/":
			filelist.write(newline+"\n")
	
	filelist.close()
	
	if not foundFiles:
		errors.append("Could not find any files in the current directory")
		ui.capTrackDestDir(capname,-1)
		return False
	
	if doGuess:
		ui.capTrackDestDir(capname,3)
		depsSucces,deps=guessDeps("var/log/captracks/"+capname)
		if depsSucces and len(deps) > 0:
			outdeps=None
			try:
				outdeps=open("var/log/captracks/"+capname+'/deps.guess','w')
			except:
				errors.append("Could not open file '%s'" % "var/log/captracks/"+capname+'/deps.guess')
				ui.capTrackDestDir(capname,-1)
				return False
			for dep in deps:
				outdeps.write(dep+"\n")
			outdeps.close()
	
	
	ui.capTrackDestDir(capname,4)
			
def capTrack(capname, ui,doGuess):
	"""
		Track an command ( usually an install ) as captrack <capname>.
		Attempts to guess dependencies as well, stores into deps.guess
	"""	
		
	ui.capTrack(capname, trackCommand, 0)
	if os.system("paco --version") == 127:
		errors.append("paco is required to track packages")
		ui.capTrack(capname, trackCommand, -1)
		return False
		
	if os.path.isdir(pacodir) == False:
		try:
			os.makedirs(pacodir)
		except KeyboardInterrupt:
			sys.exit(1)
		except:
			ui.capTrack(capname, trackCommand, -1)
			errors.append("Could not create pacodir '%s'"% pacodir)
			return False
			
	if os.path.isdir("%s/%s/%s" % (inpath,logDir,capname)):
		ui.capTrack(capname, trackCommand, -1)
		errors.append("'%s' has been tracked already, choose a different name or remove other capsule" % capname)
		return False

	try:
		os.makedirs("%s/%s/%s" % (inpath,logDir,capname))
	except KeyboardInterrupt:
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		sys.exit(1)
	except:
		ui.capTrack(capname, trackCommand, -1)
		errors.append("Could not create '%s/%s/%s" % (inpath,logDir,capname))
		return False
	
	ui.capTrack(capname, trackCommand, 1)
	os.system("paco -lp %s \"%s\"" % (capname, trackCommand))
	os.system("paco -f %s > \"%s/%s/%s/filelist\"" % (capname, inpath, logDir, capname))
	if os.system("sed -i 1d %s/%s/%s/filelist"% (inpath, logDir, capname)) != 0:
		errors.append("Package '%s' failed filelist cleansing" % capname)
		ui.capTrack(capname, trackCommand, -1)
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		return False
	if os.system("sed -i s\\/\"^\\/usr\\/man\\/\"\\/\"\\/usr\\/share\\/man\\/\"\\/ %s/%s/%s/filelist"% (inpath, logDir, capname)) != 0:
		errors.append("Package '%s' failed filelist /usr/man cleansing" % capname)
		ui.capTrack(capname, trackCommand, -1)
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		return False
	if os.system("sed -i s\\/\"^\\/usr\\/info\\/\"\\/\"\\/usr\\/share\\/info\\/\"\\/ %s/%s/%s/filelist"% (inpath, logDir, capname)) != 0:
		errors.append("Package '%s' failed filelist /usr/man cleansing" % capname)
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		ui.capTrack(capname, trackCommand, -1)
		return False
	if os.system("sed -i s\\/\"^\\/usr\\/doc\\/\"\\/\"\\/usr\\/share\\/doc\\/\"\\/ %s/%s/%s/filelist"% (inpath, logDir, capname)) != 0:
		errors.append("Package '%s' failed filelist /usr/man cleansing" % capname)
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		ui.capTrack(capname, trackCommand, -1)
		return False
	if os.system("sed -i \\/\"^\\/usr\\/info\\/dir\\/\"\\/d %s/%s/%s/filelist"% (inpath, logDir, capname)) != 0:
		errors.append("Package '%s' failed filelist /usr/man cleansing" % capname)
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		ui.capTrack(capname, trackCommand, -1)
		return False
	
	capTrackFile = None
	try:
		capTrackFile = open("%s/%s/%s/filelist" % (inpath,logDir,capname), 'r')
	except KeyboardInterrupt:
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		sys.exit(1)
	except:
		errors.append("Could not open file list for package '%s'" % capname)
		ui.capTrack(capname, trackCommand, 2)
		ui.capTrack(capname, trackCommand, -1)
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		return False
	
	firstLine = capTrackFile.readline().strip()
	capTrackFile.close()
	
	if firstLine == '':
		errors.append("Package '%s' Failed tracking" % capname)
		removeEntireDir(inpath+"/"+logDir+"/"+capname)
		ui.capTrack(capname, trackCommand, 2)
		ui.capTrack(capname, trackCommand, -1)
		return False
	
	if doGuess:
		depsSucces,deps=guessDeps("/var/log/captracks/"+capname)
		if depsSucces and len(deps) > 0:
			outdeps=None
			try:
				outdeps=open("/var/log/captracks/"+capname+'/deps.guess','w')
			except:
				errors.append("Could not open file '%s'" % "/var/log/captracks/"+capname+'/deps.guess')
			for dep in deps:
				outdeps.write(dep+"\n")
			outdeps.close()	
	
	ui.capTrack(capname, trackCommand, 3)
	return True

def makedirs(fromDir):
	"""
	     Clone directory structure and permissions from host system.
		This is used by capbuild to create full paths for a capsule.
	"""
	if os.path.isdir(fromDir) and os.path.islink(fromDir) == False:
		dirs=[]
		toDir="."
		dirs.append(fromDir)
		while fromDir != '':
			fromDir = os.path.dirname(fromDir)
			if fromDir != '/':
				dirs.append(fromDir)
			else:
				break
		dirs.reverse()
		for dir in dirs:
			if os.path.isdir(toDir+dir) == False:
				os.makedirs(toDir+dir, 0771)
				shutil.copymode(dir,toDir+dir)

def capBuild(cap, ui):
	"""
		Build a capsule from a captrack.
	"""
	numfiles=0
	counter=0
	max = 0
	
	ui.capBuild(0,None,None)
	if os.path.isdir(cap.capname):
		errors.append("'"+cap.capname+"' directory already exists")
		return False
	if os.path.exists("%s/%s/%s/filelist" % (inpath, logDir, cap.capname)) == False:
		errors.append("'%s/%s/%s/filelist' cannot be found" % (inpath, logDir, cap.capname))
		return False
	fpkg = open("%s/%s/%s/filelist" % (inpath, logDir, cap.capname), 'r')
	
	# get a line count, to tell progress
	for line in fpkg:
		if line.strip() != '':
			max+=1
	fpkg.close()
	fpkg = open("%s/%s/%s/filelist" % (inpath, logDir, cap.capname), 'r')
	
	try:
		os.makedirs(cap.capname)
		os.chdir(cap.capname)
		os.makedirs("%s/%s" % (logDir,cap.capname))
	except KeyboardInterrupt:
		sys.exit(1)
	except:
		errors.append("Could not create directory '%s/%s/%s'" % (cap.capname,logDir,cap.capname))
		return False
	
	try:
		shutil.copy2("%s/%s/%s/filelist" % (inpath,logDir,cap.capname), "%s/%s/filelist" % (logDir,cap.capname))
	except KeyboardInterrupt:
		sys.exit(1)
	except:
		errors.append("Could not copy filelist")
		return False
	
	ui.capBuild(2,0,0)
	for line in fpkg:
		if line.strip() != '':
			folder=os.path.dirname(line.strip())
			
			if os.path.isdir("."+folder) == False and os.path.islink(inpath+"/"+folder) == False:
				try:
					makedirs(folder)
				except KeyboardInterrupt:
					sys.exit(1)
				except:
					errors.append("Failed to create capsule directory '%s'" % ("."+folder))
					return False

			if os.path.islink(inpath+"/"+line.strip()):
				try:
					os.symlink(os.readlink(inpath+"/"+line.strip()),"./"+line.strip())
					counter+=1
				except KeyboardInterrupt:
					sys.exit(1)
				except:
					errors.append("Failed to symlink file "+line.strip())
			elif os.path.isdir(inpath+"/"+line.strip()):
				try:
					makedirs(line.strip())
					counter+=1
				except KeyboardInterrupt:
					sys.exit(1)
				except:
					errors.append("Failed to create directory '."+line.strip()+"'")
			elif os.path.isfile(inpath+"/"+line.strip()):
				try:
					shutil.copy2(inpath+"/"+line.strip(),"./"+line.strip())
					counter+=1
				except KeyboardInterrupt:
					sys.exit(1)
				except:
					errors.append("Failed to copy file "+inpath+"/"+line.strip())
			elif os.path.exists(inpath+"/"+line.strip()) == False:
				errors.append("'%s' doesn't exist!" % inpath+"/"+line.strip())
			
			ui.capBuild(3,counter,max)
	
	ui.capBuild(4,None,None)
	
	fpkg.close()
	
	for extra in glob.glob("%s/%s/%s/*" % (inpath,logDir,cap.capname)):
		if os.path.isfile(extra):
			try:
				shutil.copy2(extra, "%s/%s/%s" % (logDir,cap.capname,os.path.basename(extra)))
			except:
				errors.append("Couldn't copy extra file '%s'" % extra)
	
	if os.path.isfile("%s/%s/changelog" % (logDir,cap.capname)) == False:
		try:
			clog = open("%s/%s/changelog" % (logDir,cap.capname), 'w')
			clog.write(" @ "+time.strftime('%B %d, %Y')+"\n\t> Created")
			clog.close()
		except KeyboardInterrupt:
			sys.exit(1)
		except:
			errors.append("Could not create changelog")
		
	ui.capBuild(5,None,None)
	return [max,counter]

def capCompress(cap,ui):
	"""
		Compress a capsule folder into a capsule file that is distributable.
	"""
	ui.capCompress(0, None)
	if os.path.exists(cap.capname+".cap"):
		errors.append("'%s' already exists, will not overwrite" % cap.capname+".cap")
		ui.capCompress(-1, None)
		return False
	
	if os.path.isdir(cap.capname):
		if 'stripOnCompress' in options.keys() and options['stripOnCompress'] == '1':
			ui.capCompress(1, None)
			if os.system("cd %s && find . -type f -exec strip --strip-debug '{}' ';' &> /dev/null && cd .." % cap.capname) != 0:
				errors.append("Stripping of binary debug data failed!")
		
		
		ui.capCompress(2, None)
		olddir = os.getcwd()
		os.chdir(cap.capname)
		
		os.system("uname -a > %s/%s/machine" % (logDir,cap.capname))
		
		compressType = 'bz2'
		if 'compressionType' in options.keys() and options['compressionType'] == 'gz':
			compressType = 'gz'
			
		tar = tarfile.open("../"+cap.capname+".cap", "w:"+compressType)
		if 'verboseCompress' in options.keys() and options['verboseCompress'] == '1':
			for name in glob.glob('*'):
				ui.capCompressFile(name)
				tar.add(name)
		else:
			for name in glob.glob('*'):
				tar.add(name)
		tar.close()
		
		os.chdir(olddir)
		if not ui.capCompress(3, None):
			return False
		
		if 'showCompressSummary' in options.keys() and options['showCompressSummary'] == '1':
			ui.capCompress(4, None)
			ui.capCompress(5, cap)
		return True
		
	else:
		if not ui.capCompress(-1, None):
			return False
		errors.append("Cannot find directory '%s'" % cap.capname)
		return False
